import base64
import io
import math
import os
import random
import time
import uuid
from typing import List, Optional
from enum import Enum
from datetime import datetime

from PIL import Image
from pydantic import BaseModel
from apscheduler.schedulers.background import BackgroundScheduler
import socket


class BirdThreat(Enum):
    LowThreshold = 0
    NormalThreshold = 1
    HighThreshold = 2


class BirdType(str, Enum):
    SPARROW = "麻雀"
    MAGPIE = "喜鹊"
    WOODPECKER = "啄木鸟"
    EAGLE = "老鹰"
    OWL = "猫头鹰"
    PIGEON = "鸽子"
    PARROT = "鹦鹉"
    ORIOLE = "黄鹂"
    EGRET = "白鹭"
    WILD_GOOSE = "大雁"
    RAPTOR = "猛禽"

    def __str__(self):
        """
        重写__str__方法，使得在打印该枚举实例或者将其转换为字符串时，输出对应的中文鸟类名称，方便展示和使用。

        :return: 对应的中文鸟类名称。
        """
        return self.value

    @classmethod
    def from_string(cls, bird_type_str):
        """
        根据输入的表示鸟类类型的字符串获取对应的BirdType枚举实例。

        :param bird_type_str: 表示鸟类类型的字符串。
        :return: 对应的BirdType枚举实例。
        :raises ValueError: 如果输入的字符串对应的枚举实例不存在，则抛出此异常。
        """
        try:
            return cls[bird_type_str.upper()]
        except KeyError:
            raise ValueError(f"无效的鸟类类型字符串: {bird_type_str}")

    @classmethod
    def get_all_bird_types(cls):
        """
        获取所有已定义的鸟类类型枚举实例，返回一个列表，方便在需要遍历所有鸟类类型等场景下使用。

        :return: 包含所有BirdType枚举实例的列表。
        """
        return list(cls)

    @classmethod
    def get_random_image(cls, bird_type):
        """
        根据鸟类类型从对应的子文件夹中随机获取一张图像资源。

        :param bird_type: BirdType 枚举实例或表示鸟类类型的字符串。
        :return: PIL Image 对象，如果找不到图像资源返回 None。
        """
        print(bird_type)
        print(type(bird_type))
        if not isinstance(bird_type, BirdType):
            try:
                bird_type = cls.from_string(bird_type)
            except ValueError:
                print(1234)
                return None
        # 构建图像资源的路径
        image_folder = os.path.join(
            os.path.dirname(os.path.abspath(__file__)),
            "resources",
            "images",
            bird_type.name.lower(),
        )
        if os.path.exists(image_folder):
            # 获取文件夹中的所有文件
            image_files = [
                os.path.join(image_folder, f)
                for f in os.listdir(image_folder)
                if os.path.isfile(os.path.join(image_folder, f))
            ]
            if image_files:
                # 随机选择一个文件
                random_image_file = random.choice(image_files)
                try:
                    return Image.open(random_image_file)
                except Exception as e:
                    raise f"打开图像时出错: {e}"
            else:
                raise f"文件夹 {image_folder} 中没有图像文件"
        else:
            raise f"图像资源文件夹不存在: {image_folder}"

    @classmethod
    def get_random_image_base64(cls, bird_type):
        """
        根据鸟类类型从对应的子文件夹中随机获取一张图像资源，并将其转换为 base64 编码的字符串。

        :param bird_type: BirdType 枚举实例或表示鸟类类型的字符串。
        :return: base64 编码的图像字符串，如果找不到图像资源返回 None。
        """
        print(type(bird_type))
        if isinstance(bird_type, str):
            try:
                bird_type = cls.from_string(bird_type)
            except ValueError:
                return None
        # 构建图像资源的路径
        image_folder = os.path.join(
            os.path.dirname(os.path.abspath(__file__)),
            "resources",
            "images",
            bird_type.name.lower(),
        )
        print(image_folder)
        if os.path.exists(image_folder):
            # 获取文件夹中的所有文件
            image_files = [
                os.path.join(image_folder, f)
                for f in os.listdir(image_folder)
                if os.path.isfile(os.path.join(image_folder, f))
            ]
            if image_files:
                # 随机选择一个文件
                random_image_file = random.choice(image_files)
                try:
                    with Image.open(random_image_file) as img:
                        buffer = io.BytesIO()
                        if random_image_file.lower().endswith(".png"):
                            img.save(buffer, format="PNG")
                        elif random_image_file.lower().endswith(".jpg"):
                            img.save(buffer, format="JPEG")  # 保存图像到 BytesIO 对象中
                        img_byte_data = buffer.getvalue()  # 获取图像的二进制数据
                        base64_str = base64.b64encode(img_byte_data).decode(
                            "utf-8"
                        )  # 编码为 base64 字符串
                        return base64_str
                except Exception as e:
                    print(f"处理图像时出错: {e}")
                    return None
            else:
                print(f"文件夹 {image_folder} 中没有图像文件")
                return None
        else:
            print(f"图像资源文件夹不存在: {image_folder}")
            return None


class BirdInfo(BaseModel):
    track_id: str
    timestamp: str
    latitude: float
    longitude: float
    altitude: float
    classification: str
    num: int
    freq: int = 10
    v: int = 12
    threat: BirdThreat
    conflict: int = 35
    device_id: str = str(uuid.uuid4())
    imgs: Optional[List[str]] = None


class SendMessage(BaseModel):
    code: str = "0"
    msg: str = "成功"
    data: List[BirdInfo]


class Configuration(BaseModel):
    lat: float = 29.7276
    lon: float = 106.645
    radius: float = 0.5
    msg_num_sp: int = 1
    msg_num_lo: int = 1
    cycle: int = 30
    bird_lo: BirdType = BirdType.SPARROW
    bird_sp: BirdType = BirdType.OWL


def generate_random_track_id() -> str:
    """生成一个 18 位的随机轨迹 ID。"""
    return "".join(str(random.randint(0, 9)) for _ in range(18))


def generate_random_lat_lon(
        center_lat: float,
        center_lon: float,
        radius_km: float,
        min_lat: float = -90.0,  # 最小纬度
        max_lat: float = 90.0,  # 最大纬度
        min_lon: float = -180.0,  # 最小经度
        max_lon: float = 180.0,  # 最大经度
        decimal_places: int = 7,
) -> tuple[float, float]:
    """
    在给定的中心经纬度和半径范围内生成随机经纬度。
    :param center_lat: 中心纬度
    :param center_lon: 中心经度
    :param radius_km: 半径（千米）
    :param min_lat: 最小纬度
    :param max_lat: 最大纬度
    :param min_lon: 最小经度
    :param max_lon: 最大经度
    :param decimal_places: 结果的小数位数
    :return: 生成的随机经纬度元组 (latitude, longitude)
    """
    earth_radius_km = 6371.0
    center_lat_rad = math.radians(center_lat)
    center_lon_rad = math.radians(center_lon)
    random_distance = random.uniform(0, radius_km)
    random_angle = random.uniform(0, 2 * math.pi)
    lat_change_rad = random_distance / earth_radius_km
    lon_change_rad = random_distance / (earth_radius_km * math.cos(center_lat_rad))
    random_lat_rad = center_lat_rad + lat_change_rad * math.sin(random_angle)
    random_lon_rad = center_lon_rad + lon_change_rad * math.cos(random_angle)
    random_lat = round(math.degrees(random_lat_rad), decimal_places)
    random_lon = round(math.degrees(random_lon_rad), decimal_places)
    # 确保结果在指定范围内
    random_lat = max(min_lat, min(max_lat, random_lat))
    random_lon = max(min_lon, min(max_lon, random_lon))
    return random_lat, random_lon


def generate_random_altitude(min_alt: float = 10.0, max_alt: float = 100.0) -> float:
    """生成一个在指定范围内的随机海拔高度。"""
    return random.uniform(min_alt, max_alt)


def generate_random_bird_num(min_num: int = 1, max_num: int = 3) -> int:
    """生成一个在指定范围内的随机鸟类数量。"""
    return random.randint(min_num, max_num)


def generate_random_threat() -> BirdThreat:
    """生成一个随机的 BirdThreat 枚举值。"""
    threats = list(BirdThreat)
    return random.choice(threats)


def generate_bird_info(
        track_id: str,
        timestamp: str,
        longitude: float,
        latitude: float,
        altitude: float,
        bird_type: BirdType,
        bird_num: float,
) -> BirdInfo:
    """
    生成一个 BirdInfo 对象。
    :param track_id: 轨迹 ID
    :param timestamp: 时间戳
    :param longitude: 经度
    :param latitude: 纬度
    :param altitude: 海拔高度
    :param bird_type: 鸟类类型
    :param bird_num: 鸟类数量
    :return: 生成的 BirdInfo 对象
    """
    # 生成一个包含多个占位符的图像列表，实际应用中可替换为真实图像数据
    print(bird_type.get_random_image_base64(bird_type))
    print(1111)
    images = [BirdType.get_random_image_base64(bird_type) for _ in range(int(bird_num))]
    # 生成随机的威胁等级
    threat = generate_random_threat()
    return BirdInfo(
        track_id=track_id,
        timestamp=timestamp,
        latitude=latitude,
        longitude=longitude,
        altitude=altitude,
        classification=str(bird_type),
        num=int(bird_num),
        threat=threat,
        imgs=images,
    )


def build_send_message(
        sms_num: int,
        center_lon: float,
        center_lat: float,
        radius_km: float,
        msg_start: str = "ZCZC ",
        msg_end: str = " NNNN",
) -> str:
    """
    构建发送消息。
    :param sms_num: 短信数量
    :param center_lon: 中心经度
    :param center_lat: 中心纬度
    :param radius_km: 半径（千米）
    :param msg_start: 消息起始标记
    :param msg_end: 消息结束标记
    :return: 生成的 JSON 格式的消息字符串
    """

    def generate_bird_info_generator():
        for _ in range(sms_num):
            # 生成随机经纬度
            lon, lat = generate_random_lat_lon(center_lon, center_lat, radius_km)
            # 获取当前时间戳
            timestamp = datetime.now().isoformat()
            # 生成随机鸟类数量
            bird_num = generate_random_bird_num()
            # 生成一个 BirdInfo 对象
            yield generate_bird_info(
                generate_random_track_id(),
                timestamp,
                lon,
                lat,
                generate_random_altitude(),
                BirdType.OWL,
                bird_num,
            )

    # 生成所需数量的 BirdInfo 对象列表
    message_data = list(generate_bird_info_generator())
    # 构建 SendMessage 对象
    send_message = SendMessage(data=message_data)
    # 将 SendMessage 对象转换为 JSON 字符串
    message_json = send_message.model_dump_json()
    if msg_start and msg_end:
        # 如果 msg_start 和 msg_end 都存在，添加前后缀
        return msg_start + message_json + msg_end
    elif not msg_start and not msg_end:
        # 如果 msg_start 和 msg_end 都不存在，仅返回消息 JSON 字符串
        return message_json
    else:
        raise ValueError("Invalid msg_start or msg_end combination")


def retry_on_error(max_attempts: int = 3, delay: int = 1):
    """
    一个装饰器，用于在函数执行失败时进行重试。

    :param max_attempts: 最大重试次数，默认为3次。
    :param delay: 重试之间的延迟时间（秒），默认为1秒。
    :return: 装饰后的函数。
    """
    def decorator(func):
        """
        内部装饰器函数，用于包装原始函数。

        :param func: 要装饰的原始函数。
        :return: 包装后的函数。
        """
        def wrapper(*args, **kwargs):
            """
            包装后的函数，包含重试逻辑。

            :param args: 原始函数的位置参数。
            :param kwargs: 原始函数的关键字参数。
            :return: 原始函数的返回值。
            """
            attempts = 0
            while attempts < max_attempts:
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    attempts += 1
                    if attempts < max_attempts:
                        print(f"Error occurred: {e}. Retrying in {delay} second(s)...")
                        time.sleep(delay)
                    else:
                        raise

        return wrapper

    return decorator



class MessageSender:
    def __init__(self, config: Configuration, server_address: tuple[str, int]):
        self.config = config
        self.server_address = server_address
        self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.scheduler = BackgroundScheduler()
        self.local_messages = []
        self.generate_local_messages()
        self.start_scheduler()

    def generate_local_messages(self):
        def local_bird_info_generator():
            for _ in range(self.config.msg_num_lo):
                # 生成随机经纬度
                lon, lat = generate_random_lat_lon(
                    self.config.lat, self.config.lon, self.config.radius
                )
                # 获取当前时间戳
                timestamp = datetime.now().isoformat()
                # 生成随机鸟类数量
                bird_num = generate_random_bird_num()
                # 生成一个 BirdInfo 对象
                yield generate_bird_info(
                    generate_random_track_id(),
                    timestamp,
                    lon,
                    lat,
                    generate_random_altitude(),
                    BirdType.OWL,
                    bird_num,
                )

        # 生成本地消息列表
        self.local_messages = list(local_bird_info_generator())

    def build_send_message(
            self, msg_start: str = "ZCZC ", msg_end: str = " NNNN"
    ) -> str:
        """
        构建并发送消息，可指定消息起始和结束标记。
        :param msg_start: 消息起始标记
        :param msg_end: 消息结束标记
        :return: 生成的 JSON 格式的消息字符串
        """
        return build_send_message(
            self.config.msg_num_sp,
            self.config.lon,
            self.config.lat,
            self.config.radius,
            msg_start,
            msg_end,
        )

    @retry_on_error(max_attempts=3, delay=2)
    def send(self, msg_start: str = "ZCZC ", msg_end: str = " NNNN"):
        """
        发送消息的方法，可指定消息起始和结束标记，并添加了重试机制。
        :param msg_start: 消息起始标记
        :param msg_end: 消息结束标记
        """
        # 构建发送的消息
        message = self.build_send_message(msg_start, msg_end)
        print(message)
        # 发送消息
        self.client_socket.send(message.encode("utf-8"))

    def start_scheduler(self):
        # 启动定时任务，按配置的时间间隔执行 send 方法
        self.scheduler.add_job(self.send, "interval", seconds=self.config.cycle)
        self.scheduler.start()
        try:
            while True:
                time.sleep(1)
        except (KeyboardInterrupt, SystemExit):
            self.scheduler.shutdown()


if __name__ == "__main__":
    # server_address = ("192.168.227.220", 56003)
    # config = Configuration()
    # sender = MessageSender(config, server_address)
    bird = BirdType.SPARROW
    print(bird)
    base64_str = bird.get_random_image_base64(bird)
    print(base64_str)

    b = BirdType.SPARROW
    print(b)
